/* *******************************************************************************
 *   BRAIN.HH
 * *******************************************************************************/

/* *
 * Copyright (C) 2005 Atul S. Vasu
 *
 * This program is free software; you cn redistribute it and/or
* modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 * */

/* *
 * Contact Details:
 *
 * mail
 *  Atul S. Vasu, Flat No. 3, Govt Quarters, Kowdiar,
 *  Thiruvananthapuram, Kerala, India. 695003.
 * email
 *        - atulsvasu@gmail.com
 *        - atulsv@cse.iitm.ernet.in (till early 2007).
 * */


#ifndef __BRAIN_H_
#define __BRAIN_H_

#ifndef _SINGLE_
#include <vector>
#include <algorithm>
#include <cmath>
#include <ctime>
#endif

#include "engine.h"

int depth = 2;          // Search depth.
int recapture_ext = 1;  // Maximum Search extension in search of recapture.
int check_ext = 1;      // Maximum Search extension in case of check.
int pawn_7_ext = 1;     // Maximum Search extension if pawn in 7th rank.
int qui_ext = 1;	// Maximum Search extension due to evaluation pertrubation.
int time_incr = 3;      // No. of seconds that will be incremented.

const int MAX=100000;
const int WIN=50000;
const int QUI=110;
engine En;

#include "opening.h"
#include "hashtable.h"
#include "eval.h"


struct augmented {
  move M;
  int Score;
  augmented():M(OUT,OUT),Score(0) {}
};
bool operator < (const augmented& A, const augmented& B) {
  return A.Score<B.Score;
}

const int ORDER_MIN=2, TRUNC_MIN=3;

void move_order(move_list::iterator first, move_list::iterator last,int size) {
  if(depth<=ORDER_MIN)
	  return ;

  std::vector<augmented> order(size);
  std::vector<augmented>::iterator vfirst=order.begin(),vlast=order.end();

  move_list::iterator start=first;

  for(; first!=last; ++first, ++vfirst)
    {
      En.make_move(*first);
      int Sc=0;
      if(not hash_sort(Sc))
	Sc=evaluate();
      En.undo_move();

      vfirst->M = *first;
      vfirst->Score = Sc;
    }
  std::stable_sort(order.begin(),order.end());

  for(vfirst=order.begin();vfirst!=vlast; ++vfirst,++start)
    *start=vfirst->M;
}

/**
 * Static exchange evaluation.
 */
/*
int see(int Alpha, int Beta) {
  int value = evaluate();

  if (!En.l_capture()) {
    return value;
  }
  // ML is never empty because last move is a capture.
  int square(En.ML.back().to);

  move_list l;
  En.get_moves(l);
  move_list::iterator first=l.begin(),last=l.end();
  move least_move(nullMove);
  piece least_piece = BIGPIECE;

  for ( ;first != last; ++first) {
    piece p(En.P.CB[first->from]);
    if (first->to == square) {
      if (p < least_piece) {
        least_move = *first;
        least_piece = p;
      }
    }
  }

  if (least_piece != BIGPIECE) {
    En.make_move(least_move);
    int ev = -see(Alpha, Beta);
    En.undo_move();
    if (ev < value) {
      value = ev;
    }
  }

  return value;
}*/

int Q_DEPTH = 10;

int positional_eval(const move_list& l) {
  if (l.empty()) {
    return end_eval();
  } else {
    return evaluate();
  }
}

int quiesce(int Alpha, int Beta, int qdepth) {
  move_list l;
  En.get_moves(l);
  int score = positional_eval(l);
  if (qdepth == 0) {
    return score;
  }
  if (score >= Alpha) {
    return score;
  }
  if (qdepth > 1) {
    move_order(l.begin(), l.end(), l.size());
  }
  move_list::iterator first=l.begin(),last=l.end();
  move bestMove = placeHolder;
  int bbeta = Beta;
  for( ; first!=last;++first) {
    En.make_move(*first);
    if (!En.l_capture() &&
        !En.under_check() &&
        !En.pawn_7()) {
      En.undo_move();
      continue;
    }
    int e = -quiesce(-bbeta,-Alpha, qdepth - 1);
    En.undo_move();
    if(e>score) {
      score=e;
      bestMove = *first;
      if(e>bbeta) {
        bbeta=e;
        if(e>=Alpha)
          break;
      }
    }
  }
  hash_commit(Alpha, Beta, bestMove, score);
  return score;
}

int leaf(int Alpha, int Beta) {
  int score;
  if (hash_search(Alpha, Beta, score))
    return score;
  depth--;
  score = quiesce(Alpha, Beta, Q_DEPTH);
  depth++;
  return score;
}

int analyse(int Alpha, int Beta) {
  int score;
  move_list l;
  En.get_moves(l);
  if(l.empty()) {
    int score=end_eval();
    hash_commit(MAX, -MAX, nullMove, score);
    return score;
  }
  if(depth==0) {
    return leaf(Alpha, Beta);
  }

  // bool bSearchPv = true;

  int bbeta=Beta;
  move bestMove(nullMove);

  score=-MAX;
  --depth;
  if (depth > 0) {
    move_order(l.begin(), l.end(), l.size());
  }
  move_list::iterator first=l.begin(),last=l.end();
  for( ; first!=last;++first) {
    En.make_move(*first);
    int e;
    //if (bSearchPv) {
      e=-analyse(-bbeta,-Alpha);
    //} else {
    //  e=analyse(-bbeta, -bbeta-110);
    //  if (e > bbeta) {
    //    e = analyse(-bbeta, -Alpha);
    //  }
    //}
    En.undo_move();
    if(e>score) {
      score=e;
      bestMove=*first;
      if(e>bbeta) {
        bbeta=e;
        // bSearchPv = false;
        if(e>=Alpha)
          break;
      }
    }

  }
  ++depth;
  hash_commit(Alpha, Beta, bestMove, score);
  return score;
}

void print_pv(move M) {
  int i;
  mout <<M<<' ';
  En.make_move(M);
  for(i=0;i<2*depth;i++) {
    if(!hash_bestMove(M))
      break;
    if(M==nullMove) {
      mout <<'#';
      break;
    } else if (M == placeHolder) {
      mout << '?';
      break;
    }
    mout <<M<<' ';
    En.make_move(M);
  }
  while(i--) {
    En.undo_move();
  }
  En.undo_move();
  mout<<std::endl;
}
int t = time(0);
int t_bscore = 0;
int start = eval_positions;
/*
move best_move(move_list::iterator first, move_list::iterator last) {

  move m(*first);
  t_bscore = -MAX;

  for(;first!=last;++first){
    En.make_move(*first);
    int e=analyse(-t_bscore,-MAX);
    En.undo_move();
    if(e>t_bscore) {
      m=*first;
      t_bscore=e;
    }
    if (post) {
      mout << depth << " "
           << e << " "
           << time(0) - t << " "
           << eval_positions - start << " "
           << ((e == t_bscore) ? "*" : "");

      print_pv(*first);
    }
  }
  return m;
}*/

move find_best() {
  move M(OUT,OUT);

  if(refer_opening(M))// Machine-generated using database.
    return M;
  move_list l;
  En.get_moves(l);
  if(l.size()==1)
	  return *l.begin();

  if(hash_store.size()>100000)
    hash_reset();

  int SE=depth;

  if(end_game()) {
    check_ext=5;
  }
  t = time(0);

  for(depth=0;depth<SE;depth++) {
    move_order(l.begin(), l.end(),l.size());
    analyse(MAX, -MAX);
    hash_bestMove(M);
    hash_sort(t_bscore);
    if (post) {
      mout << depth << " "
           << t_bscore << " "
           << time(0) - t << " "
           << eval_positions - start << " ";

      print_pv(M);
    }
  }
  depth=SE;
  return M;
}

#endif
